// part1:C12:12.32:my_TextQuery.cpp
#include "my_TextQuery.h"
#include "../make_plural.h"
#include "my_QueryResult.h"
#include <cstddef>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstring>
#include <utility>

using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::ifstream;
using std::ispunct;
using std::istringstream;
using std::map;
using std::ostream;
using std::pair;
using std::set;
using std::shared_ptr;
using std::string;
using std::strlen;
using std::tolower;
using std::vector;

TextQuery::TextQuery(ifstream &is): file(new vector<string>)
{
  string text;
  while (getline(is, text))
  {
    file.push_back(text);
    int curr_line = file.size() - 1;
    istringstream line(text);
    string word;
    while(line >> word)
    {
      word = cleanup_str(word);
      // if word not in wm, use pos to add it.
      auto &lines = wm[word];
      if (!lines)
      {
        lines.reset(new set<line_no>);
      }
      lines->insert(curr_line);
    }
  }
}

// cleanup_str: delete punctuation.
// transform them to lowercase.
string TextQuery::cleanup_str(const string &word)
{
  string ret;
  for (auto it = word.begin(); it != word.end(); ++it)
  {
    if (!ispunct(*it))
    {
      ret += tolower(*it);
    }
  }
  return ret;
}

QueryResult
TextQuery::query(const std::string &sought) const
{
  // if not found sought, return the pointer to set.
  static shared_ptr<set<line_no>> nodata(new set<line_no>);
  auto loc = wm.find(cleanup_str(sought));

  if (loc == wm.end())
  {
    return QueryResult(sought, nodata, file); 
  }
  else
  {
    return QueryResult(sought, loc->second, file);
  }
}

ostream &print(ostream &os, const QueryResult &qr)
{
  // if find it, print total of it and line_no.
  os << qr.sought << " occurs " << qr.lines->size() << " "
    << make_plural(qr.lines->size(), "times", "s") << endl;

  for (auto num : *qr.lines)
  {
    os << "\t(line " << num + 1 << ")"
      << qr.file.begin().deref(num) << endl;
  }
  return os;
}

void TextQuery::display_map()
{
  auto iter = wm.cbegin(), iter_end = wm.cend();

  for (; iter != iter_end; ++iter)
  {
    cout << "word: " << iter->first  << " {";
  }

  // get vector, as reference of const.
  auto text_locs = iter->second;
  auto loc_iter = text_locs->cbegin(),
    loc_iter_end = text_locs->cend();

  // print word at lines_no.
  while (loc_iter != loc_iter_end)
  {
    cout << *loc_iter;
    if (++loc_iter != loc_iter_end)
    {
      cout << ", ";
    }
    cout << "}\n";
  }
  cout << endl;
}
